function MSTabControl() {

    /**
    * Css prefix pointing to the main tab control class. Prefix + specific suffix will form className of
    * desired element.
    *
    * @private
    * @property cssPrefix
    * @type     String
    * @default  TabControl
    */
    this.cssPrefix = "TabControl";

     /**
	 * Size in pixels of tab control header element
	 *
	 * @private
	 * @property  headerSize
	 * @type      Number
	 * @default   30
	 */
	this.headerSize = new Number(20);

    /**
    * Horizontal flag. Default 'true'
    *
    * @private
    * @property  horizontal
    * @type      {Boolean}
    * @default   true
    */
    this.horizontal = true;

    /**
    * Header position flag. True - header / content   False - content / header
    *
    * @private
    * @property  headerFirst
    * @type      {Boolean}
    * @default   true
    */
    this.headerFirst = true;

    /**
    * Flag idicating if buttons panel should be created
    *
    * @private
    * @property  showButtonsPanel
    * @type      {Boolean}
    * @default   false
    */
    this.showButtonsPanel = false;

    /**
    * Buttons panel DOM object reference
    *
    * @private
    * @property  buttonsPanelDom
    * @type      {DOM}
    * @default   null
    */
    this.buttonsPanelDom = null;

    /**
    * Collection of tab control buttons rendered in buttons panel (if enabled)
    *
    * @private
    * @property  buttons
    * @type      List
    * @default   empty list
    */
    this.buttons = new List();

    /**
    * Tabs header dom object reference
    *
    * @private
    * @property  headerDom
    * @type      DOM
    * @default   null
    */
    this.headerDom = null;

    /**
    * Tabs content dom object reference
    *
    * @private
    * @property  contentDom
    * @type      DOM
    * @default   null
    */
    this.contentDom = null;

    /**
    * Tab description dom object reference
    *
    * @private
    * @property  tabDescriptionDom
    * @type      DOM
    * @default   null
    */
    this.tabDescriptionDom = null;
    this.tabDescInitialized = false;
    
    /**
    * Collection of tab objects
    *
    * @private
    * @property tabs
    * @type     {List}
    * @default  empty list
    */
    this.tabs = new List();

    /**
    * Active tab reference
    *
    * @private
    * @property activeTab
    * @type     {MSTab}
    * @default  null
    */
    this.activeTab = null;

    /**
    * Initial index of active tab
    *
    * @private
    * @property activeTabIndex
    * @type     {Number}
    * @default  0
    */
    this.activeTabIndex = 0;

    

    this.CONTAINER_CSS_SUFFIX = "Container";
    // header css
    this.HEADER_TOP_CSS_SUFFIX = "HeaderTop";
    this.HEADER_BOTTOM_CSS_SUFFIX = "HeaderBottom";
    this.HEADER_LEFT_CSS_SUFFIX = "HeaderLeft";
    this.HEADER_RIGHT_CSS_SUFFIX = "HeaderRight";
    // tab wrapper css
    this.TABWRAPPER_TOP_CSS_SUFFIX ="WrapperTop";
    this.TABWRAPPER_BOTTOM_CSS_SUFFIX = "WrapperBottom";
    this.TABWRAPPER_LEFT_CSS_SUFFIX = "WrapperLeft";
    this.TABWRAPPER_RIGHT_CSS_SUFFIX = "WrapperRight";
    // buttons panel
    this.BPANEL_TOP_CSS_SUFFIX = "BPanelTop";
    this.BPANEL_BOTTOM_CSS_SUFFIX = "BPanelBottom";
    this.BPANEL_LEFT_CSS_SUFFIX = "BPanelLeft";
    this.BPANEL_RIGHT_CSS_SUFFIX = "BPanelRight";
    // tab content
    this.TAB_CONTENT_HORIZONTAL_CSS_SUFFIX = "ContentHorizontal";
    this.TAB_CONTENT_VERTICAL_CSS_SUFFIX = "ContentVertical";
    // tab description
    this.TAB_DESCRIPTION_CSS_SUFFIX = "TabDescription";

}
MSTabControl.prototype = new MessUIElement();
MSTabControl.prototype.toString = function() { return "js.mess.ui.controls.tabview.MSTabControl"; };
MessUIElement.prototype.addTagMapping("tabControl", "MSTabControl");


    /**
    * Build TabControl widget.
    *
    * @public
    * @method  build
    */
    MSTabControl.prototype.build = function() {

        // initialize control
        this.initControl();

        // build dom
        this.buildDOMObject();

        // attach event handlers
        this.attachEventHandlers();
    };

    /**
    * Attaches basic event handlers for tab control
    *
    * @private
    * @method  attachEventHandlers
    */
    MSTabControl.prototype.attachEventHandlers = function() {

        // mouse scroll
        eventUtil.addEventHandler(this.headerDom, "mousewheel", MSTabControl.prototype.onMouseScrollHandler);
    };

    /**
    * Handles mouse scroll event
    *
    * @event
    * @method onMouseScrollHandler
    */
    MSTabControl.prototype.onMouseScrollHandler = function() {

        var oEvent = eventUtil.getEvent();
		var oSource = eventUtil.getSource(oEvent);

		var tabControl = null;
		var target = MSTabControl.prototype.objects.get(oSource.id);

        if (target instanceof MSTabControl) {
        // tab control receiving event
            tabControl = target;
        }

        if (tabControl !== null) {
            // tab transition logic
            tabControl.activateNextTab();
            eventUtil.stopEvent(oEvent);
        }
    };


    /**
    * Build DOM object representation of tab control widget
    *
    * @private
    * @method   buildDOMObject
    */
    MSTabControl.prototype.buildDOMObject = function() {

        // init theme
        if (this.getTheme() === null) {
            this.setTheme("Default");
        }

        
        var container = this.buildContainerDOM();

        if (this.isHeaderFirst()) {
            // build header / content
            container.appendChild( this.buildTabsHeaderDOM());
            container.appendChild( this.buildTabsContentDOM());
        }
        else {
            // build content / header
            container.appendChild( this.buildTabsContentDOM());
            container.appendChild( this.buildTabsHeaderDOM());
        }

        this.setDomObject(container);
        this.updateControlDimensions();
    };

    /**
    * Build child tabs & buttons. Called by parent control.
    *
    * @private
    * @method  buildChildControls
    */
    MSTabControl.prototype.buildChildControls = function() {

        // buttonx box
        this.buildButtonsBox();
        // child control
        this.buildChildControl();
    };

    /**
    * Build buttons box
    *
    * @private
    * @method  buildButtonsBox
    * @param  {Boolean} reset  states if in reset mode (remove all buttons before adding new)
    */
    MSTabControl.prototype.buildButtonsBox = function(mode) {

        if (this.showButtonsPanel) {
        // okay, build buttons
            var args = MSTabControl.prototype.buildButtonsBox.arguments;
            var refresh = false;

            if (args.length === 1) {
                refresh = args[0];
            }

            if (!this.buttons.isEmpty()) {
                if (refresh) {
                // refresh buttons
                    for (var it = this.buttons.iterator(); it.hasNext();) {
                        this.refreshButton(it.next());
                    }
                }
                else {
                // build buttons from scratch
                    for (var it = this.buttons.iterator(); it.hasNext();) {
                        this.buildButton(it.next());
                    }
                }
            }
        }
    };

	/**
	 * Refresh button object - change representation according to situation
	 *
	 * @private
	 * @method   refreshButton
	 * @param    {ActionButton}   button object to refresh
	 */
	MSTabControl.prototype.refreshButton = function(button) {

		button.setButtonRepresentation();
	};

    /**
     * Build button object, performing any necessary initialization routines
     *
     * @private
     * @method  buildButton
     * @param   {ActionButton}  button object to build
     */
     MSTabControl.prototype.buildButton = function(button) {

     	button.setParent(this);
     	button.build();

        //render standard buttons in container box
    	this.buttonsPanelDom.appendChild(button.render());
     };

    /**
    * Build child control - tabs
    *
    * @private
    * @method  buildChildControl
    */
    MSTabControl.prototype.buildChildControl = function() {

        if (!this.tabs.isEmpty()) {
        // build tab objects

            // build basic tab controls
            for (var it = this.tabs.iterator(); it.hasNext();) {

                // build tab object
                var tab = this.buildChildControlObject(it.next());
                // render tab & content
                this.headerDom.firstChild.appendChild(tab.renderTab());
                this.contentDom.appendChild(tab.renderTabContent());
            }

            // build tab controls child controls
            for (var it = this.tabs.iterator(); it.hasNext();) {
                it.next().buildChildControls();
            }

            // activate tab
            if (this.activeTabIndex >= this.tabs.size()) {
                this.activeTabIndex = this.tabs.size() - 1;
            }
            else if (this.activeTabIndex < 0) {
                this.activeTabIndex = 0;
            }
            this.setActiveTab(this.tabs.get(this.activeTabIndex));
            this.tabDescInitialized = false;
        }
    };

    /**
	 * Builds tab control container DOM object
	 *
	 * @private
	 * @method  buildContainerDOM
	 * @return  {DOM} container DOM representation
	 */
	MSTabControl.prototype.buildContainerDOM = function() {

        // build container dom
        var container = DIV();
	 		container.id = this.id;
	        container.className = this.getContainerCssClass();

	 	return container;
	};

	MSTabControl.prototype.getContainerCssClass = function() {

		return this.getCssPrefix() + this.CONTAINER_CSS_SUFFIX;
	};

    /**
    * Builds header DOM object
    *
    * @private
    * @method   buildTabsHeaderDOM
    */
    MSTabControl.prototype.buildTabsHeaderDOM = function() {

        // top level header
        var header = DIV();
            header.id = this.getId();
            header.className = this.getHeaderCssClass();

            // sub header wrapping tabs
            var tabWrapper = DIV();
                tabWrapper.id = this.getId();
                tabWrapper.className = this.getTabWrapperCssClass();

                // buttons panel
                if (this.showButtonsPanel) {
                    tabWrapper.appendChild(this.buildButtonsPanelDOM());
                }

        header.appendChild(tabWrapper);
        this.headerDom = header;
        
        return header;
    };

    MSTabControl.prototype.getHeaderCssClass = function() {

        var suffix;
        if (this.isHorizontal()) {
        // get class for horizontal tab control
            if (this.isHeaderFirst()) {
                suffix = this.HEADER_TOP_CSS_SUFFIX;
            }
            else {
                suffix = this.HEADER_BOTTOM_CSS_SUFFIX;
            }
        }
        else {
        // get class for vertical tab control
            if (this.isHeaderFirst()) {
                suffix = this.HEADER_LEFT_CSS_SUFFIX;
            }
            else {
                suffix = this.HEADER_RIGHT_CSS_SUFFIX;
            }
        }
        
        return this.getCssPrefix() + suffix;
    };

    MSTabControl.prototype.getTabWrapperCssClass = function() {

        var suffix;
        if (this.isHorizontal()) {
        // get class for horizontal tab control
            if (this.isHeaderFirst()) {
                suffix = this.TABWRAPPER_TOP_CSS_SUFFIX;
            }
            else {
                suffix = this.TABWRAPPER_BOTTOM_CSS_SUFFIX;
            }
        }
        else {
        // get class for vertical tab control
            if (this.isHeaderFirst()) {
                suffix = this.TABWRAPPER_LEFT_CSS_SUFFIX;
            }
            else {
                suffix = this.TABWRAPPER_RIGHT_CSS_SUFFIX;
            }
        }

        return this.getCssPrefix() + suffix;
    };

    /**
    * Builds buttons panel DOM object
    *
    * @private
    * @method   buildButtonsPanelDOM
    */
    MSTabControl.prototype.buildButtonsPanelDOM = function() {

        var panel = DIV();
            panel.className = this.getButtonsPanelCssClass();
            this.setWidth(this.buttons.size()*12, panel);

        // render buttons

        this.buttonsPanelDom = panel;

        return panel;
    };

    MSTabControl.prototype.getButtonsPanelCssClass = function() {

        var suffix;
        if (this.isHorizontal()) {
        // get class for horizontal tab control
            if (this.isHeaderFirst()) {
                suffix = this.BPANEL_TOP_CSS_SUFFIX;
            }
            else {
                suffix = this.BPANEL_BOTTOM_CSS_SUFFIX;
            }
        }
        else {
        // get class for vertical tab control
            if (this.isHeaderFirst()) {
                suffix = this.BPANEL_LEFT_CSS_SUFFIX;
            }
            else {
                suffix = this.BPANEL_RIGHT_CSS_SUFFIX;
            }
        }

        return this.getCssPrefix() + suffix;
    };

    /**
    * Builds content DOM object
    *
    * @private
    * @method   buildTabsContentDOM
    */
    MSTabControl.prototype.buildTabsContentDOM = function() {

        // build content dom
        var content = DIV();
            content.className = this.getContentCssClass();

            // tab description div
            this.tabDescriptionDom = DIV();
            this.tabDescriptionDom.className = this.getTabDescriptionCssClass();

        content.appendChild(this.tabDescriptionDom);
        this.contentDom = content;

        return content;
    };

    MSTabControl.prototype.getContentCssClass = function() {

        var suffix;
        if (this.isHorizontal()) {
        // horizontal control
            suffix = this.TAB_CONTENT_HORIZONTAL_CSS_SUFFIX;
        }
        else {
        // vertical control
            suffix = this.TAB_CONTENT_VERTICAL_CSS_SUFFIX;   
        }

        return this.getCssPrefix() + suffix;
    };

    MSTabControl.prototype.getTabDescriptionCssClass = function() {

        var suffix = this.TAB_DESCRIPTION_CSS_SUFFIX;
        return this.getCssPrefix() + suffix;
    };

    /**
    * headerSize property setter
    *
    * @public
    * @method  setHeaderSize
    * @param   {Number} size
    */
    MSTabControl.prototype.setHeaderSize = function(size) {

        this.headerSize = size;
    };

    /**
    * headerSize property getter
    *
    * @public
    * @method  getHeaderSize
    * @return  {Number}
    */
    MSTabControl.prototype.getHeaderSize = function() {

        return this.headerSize;
    };

    /**
    * horizontal property setter
    *
    * @public
    * @method   setHorizontal
    * @param    {Boolean} mode
    */
    MSTabControl.prototype.setHorizontal = function(mode) {

        this.horizontal = mode;
    };

    /**
    * horizontal property getter
    *
    * @public
    * @method   isHorizontal
    * @return   {Boolean}
    */
    MSTabControl.prototype.isHorizontal = function() {

        return this.horizontal;    
    };

    /**
    * headerFirst property setter
    *
    * @public
    * @method   setHeaderFirst
    * @param    {Boolean} mode
    */
    MSTabControl.prototype.setHeaderFirst = function(mode) {

        this.headerFirst = mode;    
    };

    /**
    * headerFirst property getter
    *
    * @public
    * @method  isHeaderFirst
    * @return  {Boolean}
    */
    MSTabControl.prototype.isHeaderFirst = function() {

        return this.headerFirst;    
    };

    /**
    * Updates content & header dimensions
    *
    * @private
    * @method  updateControlDimensions
    */
    MSTabControl.prototype.updateControlDimensions = function() {

        // update header
        this.updateHeaderDimensions();
        // update content
        this.updateContentDimensions();
    };

    /**
    * Update header dimensions
    *
    * @private
    * @method  updateHeaderDimensions
    */
    MSTabControl.prototype.updateHeaderDimensions = function() {

        // set header size
        if (this.isHorizontal()) {
            this.setHeight(this.headerSize, this.headerDom);
            this.setHeight(this.headerSize-4, this.headerDom.firstChild); // tabs wrapper
        }
        else {
            this.setWidth(this.headerSize, this.headerDom);
            this.setWidth(this.headerSize-4, this.headerDom.firstChild); // tabs wrapper
            this.setHeight(this.getHeight(), this.headerDom);
            this.setHeight(this.getHeight(), this.headerDom.firstChild);
        }
    };
    
    /**
    * Update tab content dimensions
    *
    * @private
    * @method   updateContentDimensions
    */
    MSTabControl.prototype.updateContentDimensions = function() {

        if (this.isHorizontal()) {
        // horizontal control
            this.setHeight(this.getHeight() - this.headerSize - 1, this.contentDom);
            this.setWidth(this.getWidth(), this.contentDom);
        }
        else {
        // vertical control
            this.setHeight(this.getHeight(), this.contentDom);
            this.setWidth(this.getWidth() - this.headerSize - 1, this.contentDom);
        }
    };


    /**
    * Adds tab control child control object. Called during object initialization by
    * XmlDataParser routines.
    *
    * @public
    * @method  addControl
    * @param   {Object} control
    */
    MSTabControl.prototype.addControl = function(control) {

        if (control instanceof MSTab) {
        // child tab
            this.addTab(control);
        }
        else if (control instanceof ActionButton) {
        // tab button
            this.buttons.add(control);
        }

        return control;
    };

    /**
    * Method adds new tab to tabs collection
    *
    * @private
    * @method  addTab
    * @param   {MSTab} tab   tab to add
    */
    MSTabControl.prototype.addTab = function(tab) {

        // add tab to collection
        this.tabs.add(tab);
    };

    /**
    * Method responsible for changing tabcontrol width. Takes control type into account
    * : different logic for horizontal / vertical control.
    *
    * @public
    * @method setControlWidth
    * @param  {Number} width
    */
    MSTabControl.prototype.setControlWidth = function(width) {

        this.setWidth(width);

        if (this.isHorizontal()) {
        // horizontal control, change widths of header and content
            this.setWidth(width, this.headerDom);
            this.setWidth(width, this.contentDom);

            // update tabs
            for (var it = this.tabs.iterator(); it.hasNext();) {
                var tab = it.next();
                tab.setTabWidth(width);
            }
        }
        else if (this.isVertical()) {
        // vertical control
            this.setWidth(width - this.headerSize, this.contentDom);

            // update tabs
            for (var it = this.tabs.iterator(); it.hasNext();) {
                var tab = it.next();
                tab.setTabWidth(width - this.headerSize);
            }
        }

        this.tabDescInitialized = false;
    };

    /**
    * Method responsible for changing tabcontrol height. Takes control type into account
    * : different logic for horizontal / vertical control.
    *
    * @public
    * @method  setControlHeight
    * @param   {Number} height
    */
    MSTabControl.prototype.setControlHeight = function(height) {

        this.setHeight(height);

        if (this.isHorizontal()) {
        // horizontal control, change height of content only
            this.setHeight(height - this.headerSize, this.contentDom);

            // update tabs
            for (var it = this.tabs.iterator(); it.hasNext();) {
                var tab = it.next();
                tab.setTabHeight(height - this.headerSize);
            }
        }
        else if (this.isVertical()) {
        // vertical control, change height of header and content
            this.setHeight(height, this.headerDom);
            this.setHeight(height, this.contentDom);

            // update tabs
            for (var it = this.tabs.iterator(); it.hasNext();) {
                var tab = it.next();
                tab.setTabHeight(height);
            }
        }

        this.tabDescInitialized = false;
    };

    /**
    *
    * Return minimal height of tab control
    *
    * @public
    * @method  getMinHeight
    * @return  {Number} min height
    */
    MSTabControl.prototype.getMinHeight = function() {
        return this.headerSize;
    };

    /**
    * Active tab property getter
    *
    * @public
    * @method  getActiveTab
    * @return  {MSTab} reference to active tab
    */
    MSTabControl.prototype.getActiveTab = function() {

        return this.activeTab;
    };

    /**
    * Active tab property setter
    *
    * @public
    * @method  setActiveTab
    * @param   {MSTab} tab   tab to set as active tab
    */
    MSTabControl.prototype.setActiveTab = function(tab) {

        // deactivate active tab
        if (this.activeTab != null) {
            this.activeTab.deactivate();
        }

        this.activeTab = tab;
        tab.activate();
    };

    /**
    * Reset tab description string
    *
    * @public
    * @method  setTabDescription
    * @param   {String} desc  description string
    */
    MSTabControl.prototype.setTabDescription = function(desc) {

        // reset floating description sie & position
        if (!this.tabDescInitialized) {
            this.setWidth(this.getControlContentWidth(), this.tabDescriptionDom);
            this.setLeft(this.getOffsetLeft(this.contentDom), this.tabDescriptionDom);
            this.setTop(this.getOffsetTop(this.contentDom) + this.getControlContentHeight() - 15, this.tabDescriptionDom);

            this.tabDescInitialized = true;
        }

        if (desc === null) {
        // hide description element
            this.hide(this.tabDescriptionDom);
        }
        else {

            // set description
            this.tabDescriptionDom.innerHTML = '&nbsp;&nbsp;'+desc;
            this.show(this.tabDescriptionDom);
        }
    };

    /**
    * Return content container height
    *
    * @public
    * @method  getControlContentHeight
    * @return  {Number} height in px of window content height
    */
    MSTabControl.prototype.getControlContentHeight = function() {

        return this.getHeight(this.contentDom);
    };

    /**
    * Return content container width
    *
    * @public
    * @method  getControlContentWidth
    * @return  {Number} width in px of window content width
    */
    MSTabControl.prototype.getControlContentWidth = function() {

        return this.getWidth(this.contentDom); 
    };

    /**
    * Tab-closing logic
    *
    * @public
    * @method  closeTab
    * @param   {MSTab} tab   tab to close
    */
    MSTabControl.prototype.closeTab = function(tab) {

        var tabIdx = this.tabs.indexOf(tab);

        // remove tab
        this.tabs.removeObject(tab);
        tab.destroy();

        if (this.tabs.size() > 0) {
        // find next active tab
            var nextActiveTab = null;
            if (tabIdx < this.tabs.size()-1) {
                nextActiveTab = this.tabs.get(tabIdx);
            }
            else {
                nextActiveTab = this.tabs.last();
            }

            // activate tab
            this.setActiveTab(nextActiveTab);
        }
        else {
        // no more tabs, destroy tab control
            this.parent.close();
        }
    };

    /**
    * TabControl closing logic
    *
    * @public
    * @method  close
    */
    MSTabControl.prototype.close = function() {

        // close all tabs
        for (var it = this.tabs.iterator(); it.hasNext();) {
            var tab = it.next();
            tab.close();
        }

        this.destroy();
    };

    /**
    * ActiveTabIndex property setter
    *
    * @public
    * @method  setActiveTabIndex
    * @param   {Number} index
    */
    MSTabControl.prototype.setActiveTabIndex = function(index) {

        this.activeTabIndex = index;    
    };

    /**
    * ActiveTabIndex property getter
    *
    * @public
    * @method  getActiveTabIndex
    * @return  {Number} index
    */
    MSTabControl.prototype.getActiveTabIndex = function() {

        return this.activeTabIndex;    
    };

    /**
    * showButtonsPanel setter
    *
    * @public
    * @method  setShowButtonsPanel
    * @param   {Boolean} mode   mode to set
    */
    MSTabControl.prototype.setShowButtonsPanel = function(mode) {

        this.showButtonsPanel = mode;    
    };

    /**
    * method called via tab control 'close' button - closes active tab
    *
    * @public
    * @method  closeActiveTab
    */
    MSTabControl.prototype.closeActiveTab = function() {

        this.activeTab.closeTab();   
    };

    /**
    * method called via tab control 'next' button - activates next tab
    *
    * @public
    * @method  activateNextTab
    */
    MSTabControl.prototype.activateNextTab = function() {

        var idx = this.tabs.indexOf(this.activeTab);

        // activate next tab, with wrapping
        idx++;
        if (idx === this.tabs.size()) {
            idx = 0;
        }

        this.setActiveTab(this.tabs.get(idx));
    };

    /**
    * method called via tab control 'prev' button - activates previous tab
    *
    * @public
    * @method  activatePrevTab
    */
    MSTabControl.prototype.activatePrevTab = function() {

        var idx = this.tabs.indexOf(this.activeTab);

        // activates previous tab, with wrapping
        idx--;
        if (idx < 0) {
            idx = this.tabs.size() - 1;
        }

        this.setActiveTab(this.tabs.get(idx));
    };

